/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "rs_render_mode_config_parser.h"

#include <algorithm>
#include <fstream>

#include "platform/common/rs_log.h"

#undef LOG_TAG
#define LOG_TAG "RSRenderModeConfigParser"

namespace OHOS {
namespace Rosen {
namespace {
constexpr const char* RENDER_MODE_CONFIG_PATH = "etc/graphic/render_mode_config.xml";
}

std::shared_ptr<const RenderModeConfig> RSRenderModeConfigParser::BuildRenderConfig()
{
    RenderModeConfigBuilder builder;
    RS_LOGI("dmulti_process %{public}s: builder init", __func__);
    if (Parser(builder) != RENDER_MODE_PARSE_EXEC_SUCCESS) {
        RS_LOGE("dmulti_process %{public}s: render_mode_config parse failed", __func__);
        isMultiProcessModeEnabled_ = false;
    }
    RS_LOGI("dmulti_process %{public}s: parse successfully", __func__);
    builder.SetIsMultiProcessModeEnabled(isMultiProcessModeEnabled_);
    RS_LOGI("dmulti_process %{public}s: builder set successfully", __func__);
    return builder.Build();
}

xmlDoc* LoadConfiguration(const char* fileDir, const std::vector<std::string>& paths, bool isSys)
{
    for (const std::string& configPath : paths) {
        std::string graphicFilePath = configPath + fileDir;
        xmlDoc* doc = xmlReadFile(graphicFilePath.c_str(), nullptr, 0);
        if (doc != nullptr) {
            RS_LOGD("dmulti_process %{public}s: Success to get %{public}s graphic config: %{public}s",
                    __func__, isSys ? "sys" : "prod", graphicFilePath.c_str());
            return doc;
        }
    }
    RS_LOGE("dmulti_process %{public}s: %{public}s xmlReadFile failed", __func__, isSys ? "sys" : "prod");
    return nullptr;
}

int32_t RSRenderModeConfigParser::Parser(RenderModeConfigBuilder& builder)
{
    RS_LOGD("dmulti_process %{public}s: Parse", __func__);
    if (LoadConfiguration() != RENDER_MODE_PARSE_EXEC_SUCCESS) {
        RS_LOGE("dmulti_process %{public}s: LoadConfiguration failed", __func__);
        return RENDER_MODE_PARSE_FILE_LOAD_FAIL;
    }
    if (!xmlDocument_) {
        RS_LOGE("dmulti_process %{public}s: xmlDocument_ is empty, should do LoadConfiguration first", __func__);
        return RENDER_MODE_PARSE_FILE_LOAD_FAIL;
    }
    xmlNode* root = xmlDocGetRootElement(xmlDocument_);
    if (root == nullptr) {
        RS_LOGE("dmulti_process %{public}s: xmlDocGetRootElement failed", __func__);
        return RENDER_MODE_PARSE_GET_ROOT_FAIL;
    }

    if (!ParseInternal(*root, builder)) {
        return RENDER_MODE_PARSE_INTERNAL_FAIL;
    }

    return RENDER_MODE_PARSE_EXEC_SUCCESS;
}

bool RSRenderModeConfigParser::ParseInternal(xmlNode* node, RenderModeConfigBuilder& builder)
{
    RS_LOGD("dmulti_process %{public}s: parsing an internal node", __func__);
    xmlNode* currNode = &node;
    // TODO:
    // 1) stoi要判断是不是数字，错误处理
    isMultiProcessModeEnabled_ = std::stoi(ExtractPropertyValue("value", *currNode));
    std::string defaultGroup = ExtractPropertyValue("default_group", *currNode);
    defaultGroup.empty() ?
        builder.SetDefaultRenderProcess(DEFAULT_RENDER_PROCESS) :
        builder.SetDefaultRenderProcess(std::stoi(defaultGroup));
    
    if (currNode->xmlChildrenNode == nullptr) {
        RS_LOGD("dmulti_process %{public}s: stop parsing internal, no children nodes", __func__);
        return false;
    }

    currNode = currNode->xmlChildrenNode;
    int32_t parseSuccess = RENDER_MODE_PARSE_EXEC_SUCCESS;
    for (; currNode; currNode = currNode->next) {
        if (parseSuccess != RENDER_MODE_PARSE_EXEC_SUCCESS) {
            return false;
        }
        if (currNode->type != XML_ELEMENT_NODE) {
            continue;
        }
        if (xmlStrcmp(node.name, reinterpret_cast<const xmlChar*>("render_process"))) {
            parseSuccess = ParseGroup(*currNode, builder);
        }
    }
    return true;
}

std::string RSRenderModeConfigParser::ExtractPropertyValue(const std::string& propName, xmlNode& node)
{
    RS_LOGD("dmulti_process %{public}s: extracting value %{public}s", __func__, propName.c_str());
    std::string propValue = "";
    xmlChar* tempValue = nullptr;
    if (xmlHasProp(&node, reinterpret_cast<const xmlChar*>(propName.c_str()))) {
        tempValue = xmlGetProp(&node, reinterpret_cast<const xmlChar*>(propName.c_str()));
    }
    if (tempValue != nullptr) {
        RS_LOGD("dmulti_process %{public}s: not a empty tempValue", __func__);
        propValue = reinterpret_cast<const char*>(tempValue);
        xmlFree(tempValue);
        tempValue = nullptr;
    }
    return propValue;
}

int32_t RSRenderModeConfigParser::ParseGroup(xmlNode& node, RenderModeConfigBuilder& builder)
{
    RS_LOGD("dmulti_process %{public}s: parsing render_process", __func__);
    xmlNode* currNode = &node;
    std::string groupName = ExtractPropertyValue("name", *currNode);
    int32_t groupId = std::stoi(ExtractPropertyValue("id", *currNode));
    if (currNode->xmlChildrenNode == nullptr) {
        RS_LOGD("dmulti_process %{public}s: stop parsing internal, no children nodes", __func__);
        return false;
    }
    currNode = currNode->xmlChildrenNode;
    GroupInfoConfig groupInfo = { .groupName = groupName };
    for (; currNode; currNode = currNode->next) {
        if (currNode->type != XML_ELEMENT_NODE) {
            continue;
        }
        std::string screenName = ExtractPropertyValue("name", *currNode);
        int32_t screenId = std::stoi(ExtractPropertyValue("id", *currNode));
        RS_LOGI("dmulti_process insert screenId[%{public}d], groupId[%{public}d]", screenId, groupId);
        builder.SetScreenIdToGroupId(screenId, groupId);
        ScreenInfoConfig screenInfo = {screenName, screenId};
        groupInfo.screenInfos.emplace_back(screenInfo);
    }
    builder.SetGroupInfoConfigs(groupId, std::move(groupInfo));
    return RENDER_MODE_PARSE_EXEC_SUCCESS;
}
} // namespace Rosen
} // namespace OHOS